package com.qire.manhua.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.qire.manhua.R;

/**
 * 图片裁剪控件，用于选取裁剪图片
 */
public class CropImageView extends View {

    /**
     * IsometricZoom 等比缩放 <br />
     * SameDirectionZoom 同向等比缩放 <br />
     * FreeZoom 自由缩放
     */
    public enum ZoomMode {
        IsometricZoom, SameDirectionZoom, FreeZoom;
        public static ZoomMode build(int type) {
            switch (type) {
                case 0 :
                    return IsometricZoom;
                case 1 :
                    return SameDirectionZoom;
                case 2 :
                default:
                    return FreeZoom;
            }
        }
    }

    // 在touch重要用到的点，
    private float mX_1 = 0;
    private float mY_1 = 0;
    // 触摸事件判断
    private final int STATUS_SINGLE = 1;
    private final int STATUS_MULTI_START = 2;
    private final int STATUS_MULTI_TOUCHING = 3;
    // 当前状态
    private int mStatus = STATUS_SINGLE;
    // 默认裁剪的宽高
    private int cropWidth;
    private int cropHeight;
    // 浮层Drawable的四个点
    private final int EDGE_LT = 1;
    private final int EDGE_RT = 2;
    private final int EDGE_LB = 3;
    private final int EDGE_RB = 4;
    private final int EDGE_MOVE_IN = 5;
    private final int EDGE_MOVE_OUT = 6;
    private final int EDGE_NONE = 7;

    public int currentEdge = EDGE_NONE;

    // 缩放模式，默认是自由缩放
    protected ZoomMode zoomMode = ZoomMode.FreeZoom;
    protected float oriRationWH = 0;
    protected final float maxZoomOut = 5.0f;
    protected final float minZoomIn = 0.333333f;

    protected Drawable mDrawable;
    protected FloatDrawable mFloatDrawable;

    protected Rect mDrawableSrc = new Rect();// 图片Rect变换时的Rect
    protected Rect mDrawableDst = new Rect();// 图片Rect
    protected Rect mDrawableFloat = new Rect();// 浮层的Rect
    protected boolean isFirst = true;
    private boolean isTouchInSquare = true;

    protected Context mContext;

    public CropImageView(Context context) {
        super(context);
        init(context);
    }

    public CropImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CropImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public CropImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CropImageView, defStyleAttr, 0);
        final int zoomModeType = typedArray.getInt(R.styleable.CropImageView_zoomMode, 2);
        setZoomMode(zoomModeType);
        typedArray.recycle();
    }

    @SuppressLint("NewApi")
    private void init(Context context) {
        this.mContext = context;
        try {
            if (android.os.Build.VERSION.SDK_INT >= 11) {
                this.setLayerType(LAYER_TYPE_SOFTWARE, null);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        mFloatDrawable = new FloatDrawable(context);
    }

    public void setDrawable(Drawable mDrawable, int cropWidth, int cropHeight) {
        this.mDrawable = mDrawable;
        this.cropWidth = cropWidth;
        this.cropHeight = cropHeight;
        this.isFirst = true;
        invalidate();
    }

    public void setZoomMode(int zoomModeType) {
        setZoomMode(ZoomMode.build(zoomModeType));
    }
    public void setZoomMode(ZoomMode zoomMode) {
        this.zoomMode = zoomMode;
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (event.getPointerCount() > 1) {
            if (mStatus == STATUS_SINGLE) {
                mStatus = STATUS_MULTI_START;
            } else if (mStatus == STATUS_MULTI_START) {
                mStatus = STATUS_MULTI_TOUCHING;
            }
        } else {
            if (mStatus == STATUS_MULTI_START || mStatus == STATUS_MULTI_TOUCHING) {
                mX_1 = event.getX();
                mY_1 = event.getY();
            }

            mStatus = STATUS_SINGLE;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mX_1 = event.getX();
                mY_1 = event.getY();
                currentEdge = getTouch((int) mX_1, (int) mY_1);
                isTouchInSquare = mDrawableFloat.contains((int) event.getX(), (int) event.getY());

                break;

            case MotionEvent.ACTION_UP:
                checkBounds();
                break;

            case MotionEvent.ACTION_POINTER_UP:
                currentEdge = EDGE_NONE;
                break;

            case MotionEvent.ACTION_MOVE:
                if (mStatus == STATUS_MULTI_TOUCHING) {
                    // 多点手势触控
                } else if (mStatus == STATUS_SINGLE) {
                    int dx = (int) (event.getX() - mX_1);
                    int dy = (int) (event.getY() - mY_1);

                    mX_1 = event.getX();
                    mY_1 = event.getY();
                    // 根據得到的那一个角，并且变换Rect
                    if (!(dx == 0 && dy == 0)) {
                        switch (currentEdge) {
                            case EDGE_LT:
                                zoomDrawableFloat(dx, dy, 0, 0);
                                break;

                            case EDGE_RT:
                                zoomDrawableFloat(0, dy, dx, 0);
                                break;

                            case EDGE_LB:
                                zoomDrawableFloat(dx, 0, 0, dy);
                                break;

                            case EDGE_RB:
                                zoomDrawableFloat(0, 0, dx, dy);
                                break;

                            case EDGE_MOVE_IN:
                                if (isTouchInSquare) {
                                    moveDrawableFloat(dx, dy);
                                }
                                break;

                            case EDGE_MOVE_OUT:
                                break;
                        }
                        mDrawableFloat.sort();
                        invalidate();
                    }
                }
                break;
        }

        return true;
    }

    private void moveDrawableFloat(int dx, int dy) {
        mDrawableFloat.offset(dx, dy);
    }

    private void zoomDrawableFloat(int dx_l, int dy_t, int dx_r, int dy_b) {
        switch (zoomMode) {
            case IsometricZoom :
                int distance = dx_l == 0 ? -dx_r : dx_l;
                dx_l = distance;
                dx_r = - dx_l;
                dy_t = distance;
                dy_b = - dy_t;
                break;
            case SameDirectionZoom :
                dx_l = dx_l == 0 ? -dx_r : dx_l;
                dy_t = dy_t == 0 ? -dy_b : dy_t;
                dx_r = - dx_l;
                dy_b = - dy_t;
                break;
            case FreeZoom :
                break;
        }

        int left = mDrawableFloat.left + dx_l;
        int top = mDrawableFloat.top + dy_t;
        int right = mDrawableFloat.right + dx_r;
        int bottom = mDrawableFloat.bottom + dy_b;
        mDrawableFloat.set(left, top, right, bottom);
    }

    // 根据初触摸点判断是触摸的Rect哪一个角
    public int getTouch(int eventX, int eventY) {
        if (mFloatDrawable.checkForLeftTOPClick(eventX, eventY)) {
            return EDGE_LT;
        } else if (mFloatDrawable.checkForRightTOPClick(eventX, eventY)) {
            return EDGE_RT;
        } else if (mFloatDrawable.checkForLeftBottomClick(eventX, eventY)) {
            return EDGE_LB;
        } else if (mFloatDrawable.checkForRightBottomClick(eventX, eventY)) {
            return EDGE_RB;
        } else if (mFloatDrawable.getBounds().contains(eventX, eventY)) {
            return EDGE_MOVE_IN;
        }
        return EDGE_MOVE_OUT;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (mDrawable == null) {
            return;
        }

        if (mDrawable.getIntrinsicWidth() == 0 || mDrawable.getIntrinsicHeight() == 0) {
            return;
        }

        configureBounds();
        // 在画布上花图片
        mDrawable.draw(canvas);
        canvas.save();
        // 在画布上画浮层FloatDrawable,Region.Op.DIFFERENCE是表示Rect交集的补集
        canvas.clipRect(mDrawableFloat, Region.Op.DIFFERENCE);
        // 在交集的补集上画上灰色用来区分
        canvas.drawColor(Color.parseColor("#a0000000"));
        canvas.restore();
        // 画浮层
        mFloatDrawable.draw(canvas);
    }

    protected void configureBounds() {
        // configureBounds在onDraw方法中调用
        // isFirst的目的是下面对mDrawableSrc和mDrawableFloat只初始化一次，
        // 之后的变化是根据touch事件来变化的，而不是每次执行重新对mDrawableSrc和mDrawableFloat进行设置
        if (isFirst) {
            oriRationWH = ((float) mDrawable.getIntrinsicWidth()) / ((float) mDrawable.getIntrinsicHeight());

            final float scale = mContext.getResources().getDisplayMetrics().density;
            int w = Math.min(getWidth(), (int) (mDrawable.getIntrinsicWidth() * scale + 0.5f));
            int h = (int) (w / oriRationWH);

            int left = (getWidth() - w) / 2;
            int top = (getHeight() - h) / 2;
            int right = left + w;
            int bottom = top + h;

            mDrawableSrc.set(left, top, right, bottom);
            mDrawableDst.set(mDrawableSrc);

            int floatWidth = dip2px(mContext, cropWidth);
            int floatHeight = dip2px(mContext, cropHeight);

            if (floatWidth > getWidth()) {
                floatWidth = getWidth();
                floatHeight = cropHeight * floatWidth / cropWidth;
            }

            if (floatHeight > getHeight()) {
                floatHeight = getHeight();
                floatWidth = cropWidth * floatHeight / cropHeight;
            }

            int floatLeft = (getWidth() - floatWidth) / 2;
            int floatTop = (getHeight() - floatHeight) / 2;
            mDrawableFloat.set(floatLeft, floatTop, floatLeft + floatWidth, floatTop + floatHeight);

            isFirst = false;
        }

        mDrawable.setBounds(mDrawableDst);
        mFloatDrawable.setBounds(mDrawableFloat);
    }

    /**
     * 在up事件中调用了该方法，目的是检查是否把浮层拖出了屏幕，如果拖出屏幕的话，对选区浮层进行修正。
     * 使其永远处于控件大小范围内，如果选取大小超过控件则更具缩放模式调整到合适的缩放范围。
     */
    protected void checkBounds() {
        int newLeft = mDrawableFloat.left;
        int newTop = mDrawableFloat.top;
        int newRight;
        int newBottom;

        final int viewWidth = getWidth();
        final int viewHeight = getHeight();
        final int floatWidth = mDrawableFloat.width();
        final int floatHeight = mDrawableFloat.height();

        boolean isChange = false;
        if (mDrawableFloat.left < 0) {
            newLeft = 0;
            isChange = true;
        }

        if (mDrawableFloat.top < 0) {
            newTop = 0;
            isChange = true;
        }

        if (mDrawableFloat.right > viewWidth) {
            newLeft = viewWidth - floatWidth;
            isChange = true;
        }

        if (mDrawableFloat.bottom > viewHeight) {
            newTop = viewHeight - floatHeight;
            isChange = true;
        }

        switch (zoomMode) {
            case IsometricZoom :
                newRight = newLeft + Math.min(floatWidth, viewWidth);
                newBottom = newTop + Math.min(floatHeight, viewWidth);
                mDrawableFloat.set(newLeft, newTop, newRight, newBottom);
                break;
            case SameDirectionZoom :
                newRight = newLeft + Math.min(floatWidth, viewWidth);
                newBottom = newTop + Math.min(floatHeight, viewHeight);
                mDrawableFloat.set(newLeft, newTop, newRight, newBottom);
                break;
            case FreeZoom :
                mDrawableFloat.offsetTo(newLeft, newTop);
                break;
        }

        if (isChange) {
            invalidate();
        }
    }

    // 进行图片的裁剪，所谓的裁剪就是根据Drawable的新的坐标在画布上创建一张新的图片
    public Bitmap getCropImage() {
        Bitmap tmpBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(tmpBitmap);
        mDrawable.draw(canvas);

        Matrix matrix = new Matrix();
        float scale = (float) (mDrawableSrc.width()) / (float) (mDrawableDst.width());
        matrix.postScale(scale, scale);

        Bitmap ret = Bitmap.createBitmap(tmpBitmap, mDrawableFloat.left, mDrawableFloat.top,
                mDrawableFloat.width(), mDrawableFloat.height(), matrix, true);
        tmpBitmap.recycle();

        return ret;
    }

    public int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public class FloatDrawable extends Drawable {

        private final int cornersSize;
        private final Paint mLinePaint = new Paint();
        private final Paint mLinePaint2 = new Paint();
        {
            mLinePaint.setARGB(200, 50, 50, 50);
            mLinePaint.setStrokeWidth(1F);
            mLinePaint.setStyle(Paint.Style.STROKE);
            mLinePaint.setAntiAlias(true);
            mLinePaint.setColor(Color.WHITE);
            //
            mLinePaint2.setARGB(200, 50, 50, 50);
            mLinePaint2.setStrokeWidth(7F);
            mLinePaint2.setStyle(Paint.Style.STROKE);
            mLinePaint2.setAntiAlias(true);
            mLinePaint2.setColor(Color.WHITE);
        }

        public FloatDrawable(Context context) {
            super();
            this.cornersSize = dip2px(context, 50);
        }

        public boolean checkForLeftTOPClick(int eventX, int eventY) {
            int l = getBounds().left;
            int t = getBounds().top;
            int r = l + cornersSize;
            int b = t + cornersSize;
            return new Rect(l, t, r, b).contains(eventX, eventY);
        }

        public boolean checkForRightTOPClick(int eventX, int eventY) {
            int r = getBounds().right;
            int l = r - cornersSize;
            int t = getBounds().top;
            int b = t + cornersSize;
            return new Rect(l, t, r, b).contains(eventX, eventY);
        }

        public boolean checkForLeftBottomClick(int eventX, int eventY) {
            int l = getBounds().left;
            int r = l + cornersSize;
            int b = getBounds().bottom;
            int t = b - cornersSize;
            return new Rect(l, t, r, b).contains(eventX, eventY);
        }

        public boolean checkForRightBottomClick(int eventX, int eventY) {
            int r = getBounds().right;
            int l = r - cornersSize;
            int b = getBounds().bottom;
            int t = b - cornersSize;
            return new Rect(l, t, r, b).contains(eventX, eventY);
        }

        @Override
        public void draw(Canvas canvas) {

            int left = getBounds().left;
            int top = getBounds().top;
            int right = getBounds().right;
            int bottom = getBounds().bottom;

            final int cornersCenter = cornersSize / 2;

            Rect mRect = new Rect(left, top, right, bottom);
            mRect.inset(cornersCenter, cornersCenter);
            //画默认的选择框
            canvas.drawRect(mRect, mLinePaint);

            //画四个角的四个粗拐角、也就是八条粗线
            // 左上角
            canvas.drawLine((left + cornersCenter - 3.5f), top + cornersCenter,
                    left + cornersSize - 8f, top + cornersCenter, mLinePaint2);
            canvas.drawLine(left + cornersCenter, top + cornersCenter,
                    left + cornersCenter, top + cornersCenter + 30, mLinePaint2);
            // 右上角
            canvas.drawLine(right - cornersSize + 8f, top + cornersCenter,
                    right - cornersCenter, top + cornersCenter, mLinePaint2);
            canvas.drawLine(right - cornersCenter, top + cornersCenter - 3.5f,
                    right - cornersCenter, top + cornersCenter + 30, mLinePaint2);
            // 左下角
            canvas.drawLine((left + cornersCenter - 3.5f), bottom - cornersCenter,
                    left + cornersSize - 8f, bottom - cornersCenter, mLinePaint2);
            canvas.drawLine((left + cornersCenter), bottom - cornersCenter,
                    left + cornersCenter, bottom - cornersCenter - 30f, mLinePaint2);
            // 右下角
            canvas.drawLine((right - cornersSize + 8f), bottom - cornersCenter,
                    right - cornersCenter, bottom - cornersCenter, mLinePaint2);
            canvas.drawLine((right - cornersCenter), bottom - cornersCenter - 30f,
                    right - cornersCenter, bottom - cornersCenter + 3.5f, mLinePaint2);
        }

        @Override
        public void setBounds(Rect bounds) {
            Rect tmpBounds = new Rect(bounds);
            int offset = cornersSize / 2;
            tmpBounds.inset(-offset, -offset);
            super.setBounds(tmpBounds);
        }

        @Override
        public void setAlpha(int alpha) {

        }

        @Override
        public void setColorFilter(ColorFilter cf) {

        }

        @Override
        public int getOpacity() {
            return PixelFormat.UNKNOWN;
        }
    }

}